package service

import (
	"crypto/ecdsa"
	"encoding/hex"
	"errors"
	"log/slog"
	"raymond/api/bnb"
	"raymond/api/tron"
	"raymond/global"
	"raymond/model"
	"raymond/util"
	"time"

	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcutil/hdkeychain"
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/jmoiron/sqlx"
	"github.com/mr-tron/base58"
	"github.com/tyler-smith/go-bip39"
	"golang.org/x/crypto/sha3"
)

const table_bsn_main_wallets = "t_bsn_main_wallets"
const table_bsn_contract_wallets = "t_bsn_contract_wallets"

func SaveWallet(wallet *WalletV1) (id int64, err error) {
	bnbContractList := bnb.GetBep20ContractList()

	tronContractList := tron.GetTrc20ContractList()

	if bnbContractList == nil || tronContractList == nil || len(bnbContractList) <= 0 || len(tronContractList) <= 0 {
		return 0, errors.New("未配置代币合约")
	}

	tx, err := global.DB().Beginx()
	if err != nil {
		return 0, err
	}

	defer func() {
		if p := recover(); p != nil {
			tx.Rollback()
			panic(p) // rethrow panic after rollback
		} else if err != nil {
			slog.Warn("rollback", "GenerateWallet", err.Error())
			tx.Rollback()
		} else {
			slog.Info("commit", "GenerateWallet", "ok")
			tx.Commit()
		}
	}()

	now := time.Now().UTC()
	mw := &model.BSNMainWallet{
		Priv:       wallet.PrivateKeyHex,
		BNBAddr:    wallet.ETHAddress,
		TRONAddr:   wallet.TronAddress,
		Mnemonics:  wallet.Mnemonics,
		CreateTime: now,
		UpdateTime: now,
	}

	result, err := tx.NamedExec("insert into "+table_bsn_main_wallets+" (priv, bnb_addr, tron_addr, mnemonics, create_time, update_time)"+
		" values (:priv,:bnb_addr,:tron_addr,:mnemonics,:ct,:ut)",
		&mw)
	if err != nil {
		return 0, err
	}
	id, _ = result.LastInsertId()

	var cws []model.BSNContractWallet
	for _, contract := range bnbContractList {
		cw := &model.BSNContractWallet{
			MainWalletId: id,
			Type:         string(global.BNB),
			Addr:         mw.BNBAddr,
			Contract:     contract,
		}
		cws = append(cws, *cw)
	}

	for _, contract := range tronContractList {
		cw := &model.BSNContractWallet{
			MainWalletId: id,
			Type:         string(global.TRC),
			Addr:         mw.TRONAddr,
			Contract:     contract,
		}
		cws = append(cws, *cw)
	}

	// 批量新增合约钱包
	insert := "insert into " + table_bsn_contract_wallets +
		" (main_wallet_id, type, addr, contract, create_time, update_time) values "

	vals := []interface{}{}
	for _, cw := range cws {
		insert += "(?,?,?,?,?,?),"
		vals = append(vals, cw.MainWalletId, cw.Type, cw.Addr, cw.Contract, now, now)
	}
	insert = insert[0 : len(insert)-1]

	insert, args, err := sqlx.In(insert, vals...)
	if err != nil {
		return 0, err
	}

	insert = tx.Rebind(insert)

	_, err = tx.Exec(insert, args...)
	if err != nil {
		return 0, err
	}

	return id, nil
}

type WalletV1 struct {
	PrivateKeyHex string
	Mnemonics     string
	ETHAddress    string
	TronAddress   string
}

// 添加助记词
func GenerateWalletV1() (*WalletV1, error) {
	// 生成助记词
	length := 12
	entropy, err := bip39.NewEntropy(length / 3 * 32)
	if err != nil {
		return nil, err
	}
	mnemonic, err := bip39.NewMnemonic(entropy)
	if err != nil {
		return nil, err
	}
	seed, err := bip39.NewSeedWithErrorChecking(mnemonic, "")
	if err != nil {
		return nil, err
	}
	extended, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
	if err != nil {
		return nil, err
	}
	pr, err := extended.ECPrivKey()
	if err != nil {
		return nil, err
	}
	// 根据助记词生成私钥
	privateKey := pr.ToECDSA()

	// 转化为字节
	privateKeyBytes := crypto.FromECDSA(privateKey)
	// 转化为十六进制字符串,并删除0x前缀
	privateHex := hexutil.Encode(privateKeyBytes)[2:]
	//fmt.Println("pr", privateHex)

	// 从私钥生成公钥
	publicKey := privateKey.Public()
	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
	if !ok {
		return nil, errors.New("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
	}
	publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
	ethAddress := generateETHAddress(publicKeyBytes)
	tronAddress := generateTronAddress(publicKeyBytes)

	wallet := WalletV1{
		PrivateKeyHex: privateHex,
		ETHAddress:    ethAddress,
		TronAddress:   tronAddress,
		Mnemonics:     mnemonic,
	}
	return &wallet, nil
}

func generateETHAddress(publicKeyBytes []byte) string {
	hash := sha3.NewLegacyKeccak256()
	hash.Write(publicKeyBytes[1:])
	address := hexutil.Encode(hash.Sum(nil)[12:])
	return address
}

func generateTronAddress(publicKeyBytes []byte) string {
	hash := sha3.NewLegacyKeccak256()
	hash.Write(publicKeyBytes[1:])
	address := hexutil.Encode(hash.Sum(nil)[12:])
	address = "41" + address[2:]
	addb, _ := hex.DecodeString(address)
	hash1 := util.SHA256(util.SHA256(addb))
	secret := hash1[:4]
	for _, v := range secret {
		addb = append(addb, v)
	}
	return base58.Encode(addb)
}

/*
func generateETHAddress0(publicKeyECDSA *ecdsa.PublicKey) string {
	address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
	return address
}

func generateETHPublicKey(publicKeyBytes []byte) string {
	return hexutil.Encode(publicKeyBytes)[4:]
}

func generateTronAddress0(publicKeyECDSA *ecdsa.PublicKey) string {
	address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
	address = "41" + address[2:]
	addb, _ := hex.DecodeString(address)
	hash1 := util.SHA256(util.SHA256(addb))
	secret := hash1[:4]
	for _, v := range secret {
		addb = append(addb, v)
	}
	return base58.Encode(addb)
}

func generateTronPublicKey(publicKeyBytes []byte) string {
	return hexutil.Encode(publicKeyBytes)[2:]
}
*/
